/*
 * the PLySubtransaction class
 *
 * src/pl/plpython/plpy_subxactobject.c
 */

#include "postgres.h"

#include "access/xact.h"
#include "plpy_elog.h"
#include "plpy_subxactobject.h"
#include "plpython.h"
#include "utils/memutils.h"

List	   *explicit_subtransactions = NIL;


static void fc_PLy_subtransaction_dealloc(PyObject *fc_subxact);
static PyObject *fc_PLy_subtransaction_enter(PyObject *fc_self, PyObject *fc_unused);
static PyObject *fc_PLy_subtransaction_exit(PyObject *fc_self, PyObject *fc_args);

static char PLy_subtransaction_doc[] =
"PostgreSQL subtransaction context manager";

static PyMethodDef PLy_subtransaction_methods[] = {
	{"__enter__", fc_PLy_subtransaction_enter, METH_VARARGS, NULL},
	{"__exit__", fc_PLy_subtransaction_exit, METH_VARARGS, NULL},
	/* 用户友好的 Python <2.6 的名称 */
	{"enter", fc_PLy_subtransaction_enter, METH_VARARGS, NULL},
	{"exit", fc_PLy_subtransaction_exit, METH_VARARGS, NULL},
	{NULL, NULL, 0, NULL}
};

static PyTypeObject PLy_SubtransactionType = {
	PyVarObject_HEAD_INIT(NULL, 0)
	.tp_name = "PLySubtransaction",
	.tp_basicsize = sizeof(PLySubtransactionObject),
	.tp_dealloc = fc_PLy_subtransaction_dealloc,
	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
	.tp_doc = PLy_subtransaction_doc,
	.tp_methods = PLy_subtransaction_methods,
};


void PLy_subtransaction_init_type(void)
{
	if (PyType_Ready(&PLy_SubtransactionType) < 0)
		elog(ERROR, "could not initialize PLy_SubtransactionType");
}

/* s = plpy.subtransaction() */
PyObject * PLy_subtransaction_new(PyObject *fc_self, PyObject *fc_unused)
{
	PLySubtransactionObject *fc_ob;

	fc_ob = PyObject_New(PLySubtransactionObject, &PLy_SubtransactionType);

	if (fc_ob == NULL)
		return NULL;

	fc_ob->started = false;
	fc_ob->exited = false;

	return (PyObject *) fc_ob;
}

/* Python 需要定义一个 dealloc 函数 */
static void fc_PLy_subtransaction_dealloc(PyObject *fc_subxact)
{
}

/*
 * subxact.__enter__() 或 subxact.enter()
 *
 * 开始一个显式的子事务。显式子事务中的 SPI 调用不会启动另一个，
 * 因此您可以原子地执行多个 SPI 调用，如果其中一个失败，
 * 仍然可以获得可控的异常。
 */
static PyObject * fc_PLy_subtransaction_enter(PyObject *fc_self, PyObject *fc_unused)
{
	PLySubtransactionData *fc_subxactdata;
	MemoryContext fc_oldcontext;
	PLySubtransactionObject *fc_subxact = (PLySubtransactionObject *) fc_self;

	if (fc_subxact->started)
	{
		PLy_exception_set(PyExc_ValueError, "this subtransaction has already been entered");
		return NULL;
	}

	if (fc_subxact->exited)
	{
		PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
		return NULL;
	}

	fc_subxact->started = true;
	fc_oldcontext = CurrentMemoryContext;

	fc_subxactdata = (PLySubtransactionData *)
		MemoryContextAlloc(TopTransactionContext,
						   sizeof(PLySubtransactionData));

	fc_subxactdata->oldcontext = fc_oldcontext;
	fc_subxactdata->oldowner = CurrentResourceOwner;

	BeginInternalSubTransaction(NULL);

	/* 确保 explicit_subtransactions 列表中的单元是长期存在的 */
	MemoryContextSwitchTo(TopTransactionContext);
	explicit_subtransactions = lcons(fc_subxactdata, explicit_subtransactions);

	/* 调用者想要保持在原始内存上下文中 */
	MemoryContextSwitchTo(fc_oldcontext);

	Py_INCREF(fc_self);
	return fc_self;
}

/*
 * subxact.__exit__(exc_type, exc, tb) 或 subxact.exit(exc_type, exc, tb)
 *
 * 退出显式子事务。exc_type 是异常类型，exc 是异常对象，tb 是回溯。
 * 如果 exc_type 为 None，则提交子事务；如果不是，则中止它。
 *
 * 方法签名选择允许子事务对象作为上下文管理器使用，如
 * <http://www.python.org/dev/peps/pep-0343/> 中所述。
 */
static PyObject * fc_PLy_subtransaction_exit(PyObject *fc_self, PyObject *fc_args)
{
	PyObject   *fc_type;
	PyObject   *fc_value;
	PyObject   *fc_traceback;
	PLySubtransactionData *fc_subxactdata;
	PLySubtransactionObject *fc_subxact = (PLySubtransactionObject *) fc_self;

	if (!PyArg_ParseTuple(fc_args, "OOO", &fc_type, &fc_value, &fc_traceback))
		return NULL;

	if (!fc_subxact->started)
	{
		PLy_exception_set(PyExc_ValueError, "this subtransaction has not been entered");
		return NULL;
	}

	if (fc_subxact->exited)
	{
		PLy_exception_set(PyExc_ValueError, "this subtransaction has already been exited");
		return NULL;
	}

	if (explicit_subtransactions == NIL)
	{
		PLy_exception_set(PyExc_ValueError, "there is no subtransaction to exit from");
		return NULL;
	}

	fc_subxact->exited = true;

	if (fc_type != Py_None)
	{
		/* 中止内部事务 */
		RollbackAndReleaseCurrentSubTransaction();
	}
	else
	{
		ReleaseCurrentSubTransaction();
	}

	fc_subxactdata = (PLySubtransactionData *) linitial(explicit_subtransactions);
	explicit_subtransactions = list_delete_first(explicit_subtransactions);

	MemoryContextSwitchTo(fc_subxactdata->oldcontext);
	CurrentResourceOwner = fc_subxactdata->oldowner;
	pfree(fc_subxactdata);

	Py_RETURN_NONE;
}
